package core.reports;

import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.itextpdf.text.DocumentException;

import core.Amount;
import core.CoreFacade;
import core.ProductGroup;
import core.StorageUnit;
import core.ProductContainer;
import core.Product;
import core.Unit;

/**
 * This class will create the Notices Report:
 * It will be responsible for getting the necessary data
 * through the visitor pattern, and then constructing the report
 * through the builder pattern.
 * 
 */
public class NoticesGuide implements IDirectorVisitor 
{
	/**
	 * This class is used to copare two containerProduct pair classes.
	 * @author Spencer
	 *
	 */
	private class MyObjComparable implements Comparator<ContainerProductPair>{
		 
	    @Override
	    public int compare(ContainerProductPair o1, ContainerProductPair o2) 
	    {
	    	return o1.product.getDescription().compareTo(o2.product.getDescription());
	    }
	}
	
	/**
	 * This class is used to compare two product group objects.
	 * @author Spencer
	 *
	 */
	private class MyPGComparable implements Comparator<ProductGroup>{
		 
	    @Override
	    public int compare(ProductGroup o1, ProductGroup o2) 
	    {
	    	return o1.getName().compareTo(o2.getName());
	    }
	}
	
	/** keeps tracke of all the products in the container and below */
	private Map<ProductContainer, ArrayList<ContainerProductPair>> containersToProducts;
	
	/** Keeps track of all inconsistancies */
	private Map<ProductContainer, ArrayList<ContainerProductPair>> inconsistancies;
	
	/** A list of all the product groups that need to be printed */
	private ArrayList<ProductGroup> toPrint;
	
	/**
	 * Default Constructor
	 */
	public NoticesGuide()
	{
		containersToProducts = new HashMap<ProductContainer, ArrayList<ContainerProductPair>>();
		inconsistancies = new HashMap<ProductContainer, ArrayList<ContainerProductPair>>();
		toPrint = new ArrayList<ProductGroup>();
	}
	
	@Override
	/**
	 * When creating the Notices report, this method will 
	 * get the necessary information from productGroups
	 * @param pg the productGroup
	 * @return true if it was successful
	 */
	public boolean visitProductGroup(ProductGroup pg, TraversalOrder order) 
	{
		boolean success = false;
		
		if (order == TraversalOrder.PostOrder)
		{
			success = visitProductGroupPost(pg);
		}
		else if (order == TraversalOrder.PreOrder)
		{
			success = visitProductGroupPre(pg);
		}
		else
		{
			//Should never get here
			success = false;
		}
		
		return success;
	}

	/**
	 * This method is called when the 'visit' method has the order value
	 * of Preorder.  This does all the calculations needed on a preorder
	 * visit of the node.  This means that it will compare the needed
	 * supply with the supply we currently have.  If needed > have then 
	 * the Product is added to a list of containers that don't have a 
	 * sufficent supply for the number of months.
	 * 
	 * @param pg -- The product Group that we are visiting.
	 * @return True
	 */
	private boolean visitProductGroupPre(ProductGroup pg)
	{
		ArrayList<ContainerProductPair> prob = new ArrayList<ContainerProductPair>();
		
		ArrayList<ContainerProductPair> p = containersToProducts.get(pg);
		
		if (pg.getThreeMonthSupplyAmount().getUnit() == Unit.count) return true;
		
		for (int i = 0; i < p.size(); i++)
		{
			ContainerProductPair cpp = p.get(i);
			
			if (pg.getThreeMonthSupplyAmount().isCompatableAmounts(
					cpp.product.getAmount()) == false)
			{
				prob.add(cpp);
			}
		}
		
		if (prob.size() > 0)
		{
			Collections.sort(prob, new MyObjComparable());
			
			this.inconsistancies.put(pg, prob );
			
			toPrint.add(pg);
		}
		
		return true;
	}
	
	/**
	 * This method is called when a visit method has an order parameter
	 * of PostOrder.  This will calculate the amount of supply contained
	 * within a Product Group and within all of it's children producct
	 * groups.  It converts the amount of the product to the amount of
	 * the product group and adds it to the total.
	 * 
	 * @param pg
	 * @return
	 */
	private boolean visitProductGroupPost(ProductGroup pg)
	{	
		ArrayList<ContainerProductPair> products = new ArrayList<ContainerProductPair>();
		
		Set<Product> p = CoreFacade.getInstance().getAllProducts(pg);
		Iterator it = p.iterator();
		
		while(it.hasNext())
		{
			Product curP = (Product) it.next();
			
			products.add(new ContainerProductPair(pg, curP));
			
			Collections.sort(products, new MyObjComparable());
		}
		
		List<ProductGroup> children = pg.getProductGroups();
		
		Collections.sort(children, new MyPGComparable());
		
		for (int i = 0; i < children.size(); i++)
		{
			products.addAll(containersToProducts.get(children.get(i)));
		}
		
		containersToProducts.put(pg, products);
		
		return true;
	}
	
	@Override
	/**
	 * When creating the Notices report, this method will 
	 * get the necessary information from StorageUnits
	 * @param su the storage unit
	 * @return true if it was successful
	 */
	public boolean visitStorageUnit(StorageUnit su, TraversalOrder order) 
	{
		return false;
	}

	
	@Override
	/**
	 * After getting the data from the visit methods, this will
	 * then instruct the appropriate builder how to create the 
	 * necessary report
	 * @return true if it was successful
	 */
	public String construct(String s) 
	{
		IBuilder report = null;
		
		if (s == "HTML")
		{
			System.out.println("HTML TYPE");
			report = new HTMLBuilder("reports/N-MonthSupply-" + new Date().getTime() + ".html");
		}
		else if (s == "DATA")
		{
			System.out.println("DATA TYPE");
		}
		else //if (s == "PDF")
		{
			System.out.println("PDF TYPE");
			report = new PDFBuilder("reports/N-MonthSupply-" + new Date().getTime() + ".pdf");
		}
		
		report.addTitle("Notices");
		
		report.addBlankLine();
		report.addBlankLine();
		
		if (toPrint.size() == 0)
		{
			report.addParagraph("There are no Notices at this time.");
		}
		else
		{
			report.addSubTitle("3-Month Supply Warnings");
			
			for (int i = 0; i < toPrint.size(); i++)
			{
				ArrayList<ContainerProductPair> cpp = inconsistancies.get(toPrint.get(i));
				
				report.addParagraph("Product Group " + 
				CoreFacade.getInstance().getParentStorageUnit(toPrint.get(i)).getName()
						+ "::" + toPrint.get(i).getName() + " has a 3-Month supply (" + 
				toPrint.get(i).getThreeMonthSupplyAmount().toString()
						+ ") that is inconsistent with the following products:");
				
				report.addList();
				
				for (int j = 0; j < cpp.size(); j++)
				{
					ContainerProductPair c = cpp.get(j);
					report.addListItem(c.container.getName() + "::" + c.product.getDescription() +
							" " + c.product.getAmount().toString());
				}
				
				report.closeCurrentList();
				report.addBlankLine();
			}
		}
		
		report.finish();
		
		if (s == "HTML")
		{
			return ((HTMLBuilder)report).getFileName();
		}
		else if (s == "DATA")
		{
			System.out.println("DATA TYPE");
			return "";
		}
		else //if (s == "PDF")
		{
			return ((PDFBuilder)report).getFileName();
		}
	}
}
